#pragma once
#include <queue>
#include <iostream>
#include <utility>

#include "Cond.hpp"
#include "Mutex.hpp"

using namespace CondModule;
using namespace MutexModule;

const int defaultcap = 5;

template <class T>
class BQ
{
private:
    bool IsFull() { return _q.size() == _capacity; }
    bool IsEmpty() { return _q.empty(); }

public:
    BQ(int capacity = defaultcap)
        : _capacity(capacity), _psleep(0), _csleep(0)
    {
    }

    template <class T1>
    void Equeue(T1 &&t)
    {
        { // 访问临界资源加锁
            MutexGuard mg(_mutex);

            // 队列满，进入条件变量等待
            // 可能被伪唤醒，所以要以判断为标准，而不是条件变量，条件变量只是避免忙等浪费 CPU 资源
            while (IsFull())
            {
                _psleep++;
                _full_cond.Wait(_mutex);
                _psleep--;
            }

            _q.push(std::forward<T1>(t));

            if (_csleep)
            {
                std::cout << "唤醒了一个消费者" << std::endl;
                _empty_cond.Notify();
            }
        }
    }

    T Pop()
    {
        T ret;
        {
            MutexGuard mg(_mutex); //加锁与释放与临时变量生命周期绑定
            while (IsEmpty())
            {
                _csleep++;
                _empty_cond.Wait(_mutex);
                _csleep--;
            }

            ret = std::move(_q.front());
            _q.pop();

            if (_psleep)
            {
                std::cout << "唤醒了一个生产者" << std::endl;
                _full_cond.Notify();
            }
        }
        return ret;
    }

    ~BQ()
    {
    }

private:
    std::queue<T> _q;
    int _capacity;
    MutexModule::Mutex _mutex;
    CondModule::Cond _full_cond;
    CondModule::Cond _empty_cond;
    int _psleep;
    int _csleep;
};